MarkdownVerbs.php

<?php

namespace Tlf\Scrawl\Ext\DefaultExt;

/**
 * Runs `@mdverb` extensions, enabling special callbacks for `@verb()`s in `.src.md` files
 */
class MarkdownVerbs extends \Tlf\Scrawl\ExtensionType\DefaultExt {

    protected $regData = [
        'verb' =>'/(?<!\\\\)\@([a-zA-Z_]+)\(([^\)]*)\)/m',
    ];

    /**
     * Array mapping verbs to callables `['verb'=>[ $callable1, $callable2, $callable3], 'anotherverb'=>[...] ]`
     */
    protected array $verbMap = [];

    /**
     * Setup the verb map
     */
    public function onBuildStart(){
        $exts = $this->scrawl->getExtensions('mdverb');
        foreach ($exts as $ext){
            $verbs = $ext->getVerbs();
            foreach ($verbs as $verb=>$method){
                $this->verbMap[$verb][] = [$ext, $method];
            }
        }
    }

    /**
     * Find verbs in a source md file
     */
    public function onTemplateFileFound(\Tlf\Scrawl\File $templateFile){
        $this->match('verb',$templateFile,$templateFile);
        $this->writeMdFile($templateFile);
    }

    /**
     * Execute md verb extension methods & update output-content
     */
    public function matchVerb($info, $templateFile, $verb, $argListStr){
        $file = $templateFile;
        $line = $info['match'][0];
        $parsedArgs = $this->parseArgListString($argListStr);
        $info = [
            'file'=>$file,
            'line'=>$line,
            'verb'=>$verb,
            'argListStr'=>$argListStr,
            'argList' => $parsedArgs,
        ];
        foreach ($this->verbMap[$verb]??[] as $callable){
            // $ret = $callable($verb, $argListStr, $file, $line);
            try {
                $ret = $callable($info, ...$parsedArgs);
            } catch (\ArgumentCountError $e){
                $class = get_class($callable[0]);
                $method = $callable[1];
                echo "\n\n";
                echo "\nCannot call $class->$method() becuase of an argument count error.";

                echo "\n\n";
                echo "Error caused by: \n";
                $info = (array)$info;
                $info['file'] = (array)$info['file'];
                $info['file']['content'] = '--content is removed from var dump for readability--';
                print_r($info);

                echo "\n\n";
                throw $e;
            }
            if ($ret===false)continue;
            $file->content = str_replace($line, $ret, $file->content());
        }
    }

    /**
     * Append the correct md file to the output, to later be written by Scrawl
     */
    public function writeMdFile(\Tlf\Scrawl\File $templateFile){
        $relPath = $templateFile->relPath;
        $ext = '.src.md';
        $len = strlen($ext);
        if (substr($relPath, -$len)==$ext){
            $relPath = substr($relPath,0,-$len).'.md';
        }

        if ($this->scrawl->getConfig('markdown.preserveNewLines')[0]){
            // @TODO add test for markdown.preserveNewLines
            $templateFile->content = str_replace("\n","  \n",$templateFile->content());
        }

        if ($this->scrawl->getConfig('markdown.prependGenNotice')[0]){
            // @TODO give relative path to source file
            $templateFile->content = "<!-- DO NOT EDIT. This file generated from template by Code Scrawl https://tluf.me/code-scrawl/ -->\n".$templateFile->content();
        }
        
        $templateDir = $this->scrawl->getConfig('dir.template')[0];

        $this->scrawl->addOutput('file', $relPath, $templateFile->content());
    }

    /**
     * Parse an arg list string & return it as an array
     *
     * Each arg is trimmed. There is no special handling of any character. Commas may not appear in arguments, as they are argument separators.
     *
     * @param $argListString a string like `arg1, arg2, arg3` where every arg is separated by a comma. 
     *
     */
    public function parseArgListString(string $argListString): array{
        $list = explode(',', $argListString);
        $list = array_map('trim', $list);
        return $list;
    }

}